﻿namespace TestClassLibrary
{
	using System;
	using System.Collections.Generic;

	public class PublicTestClass
	{
		static private int StaticPrivateField;
		static protected int StaticProtectedField;
		static public int StaticPublicField;
		static internal int StaticInternalField;
		static protected internal int StaticProtectedInternalField;

		static private int StaticPrivateProperty { get; set; }
		static protected int StaticProtectedProperty { get; set; }
		static public int StaticPublicProperty { get; set; }
		static internal int StaticInternalProperty { get; set; }
		static protected internal int StaticProtectedInternalProperty { get; set; }

		static int count; // counts method calls

		static private Accessors StaticPrivateMethod() { ++count; return Accessors.StaticPrivate; }
		static protected Accessors StaticProtectedMethod() { ++count; return Accessors.StaticProtected; }
		static public Accessors StaticPublicMethod() { ++count; return Accessors.StaticPublic; }
		static internal Accessors StaticInternalMethod() { ++count; return Accessors.StaticInternal; }
		static internal protected Accessors StaticInternalProtectedMethod() { ++count; return Accessors.StaticInternalProtected; }

		private int PrivateField;
		protected int ProtectedField;
		public int PublicField;
		internal int InternalField;
		protected internal int ProtectedInternalField;

		private int PrivateProperty { get; set; }
		protected int ProtectedProperty { get; set; }
		public int PublicProperty { get; set; }
		internal int InternalProperty { get; set; }
		protected internal int ProtectedInternalProperty { get; set; }

		private Accessors PrivateMethod() { ++count; return Accessors.Private; }
		protected Accessors ProtectedMethod() { ++count; return Accessors.Protected; }
		public Accessors PublicMethod() { ++count; return Accessors.Public; }
		internal Accessors InternalMethod() { ++count; return Accessors.Internal; }
		internal protected Accessors InternalProtectedMethod() { ++count; return Accessors.InternalProtected; }

		static PublicTestClass() { }
		public PublicTestClass() { }
		public PublicTestClass(PublicConstructor x) { }
		protected PublicTestClass(ProtectedConstructor x) { }
		private PublicTestClass(PrivateConstructor x) { }
		internal PublicTestClass(InternalConstructor x) { }
		protected internal PublicTestClass(ProtectedInternalConstructor x) { }

		// not complete, but sufficient? (tests both private/private and public/private)
		protected int ProtectedGetPrivateSetProperty { get; private set; }
		public int PublicGetPrivateSetProperty { get; private set; }
		
		// idem (tests readonly fields)
		private readonly double PrivateReadonlyField;
		public readonly double PublicReadonlyField;

		PrivateTestClass PrivateTypeReferencingField;
		PrivateTestClass PrivateTypeReferencingProperty { get; set; }
		PrivateTestClass PrivateTypeReferencingMethod(PrivateTestClass x) { return x; }
		PublicTestClass SelfReference;

		// this is very tedious to wrap correctly
		// taking first (most specialized?) non-generic interface good workaround?
		List<PrivateTestClass> GenericField;
		List<PrivateTestClass> GenericProperty { get; set; }
		List<PrivateTestClass> GenericMethod(List<PrivateTestClass> x) { return x; }

		Type GenericMethod2<Type>(Type arg) { return arg; }
		Type GenericMethod3<Type>(Type arg, int arg2) { return arg; }
		
		// unsupported (at runtime, not code generation time)
		// it seems pretty much impossible to get MethodInfo for this one (except looping through all methods ofc)
		//Type GenericMethod2<Type>(int arg1, Type arg2) { return arg2; }

		// ref/out keywords not supported by System.CodeDom
		void ByRefMethod(ref int arg) { }
		void OutMethod(out int arg) { arg = 10; }
		void GenericByRefMethod<T>(ref int arg1, T arg2) { }
		void GenericByRefMethod2<T>(ref T arg1, out T arg2) { arg2 = arg1; }

		// this is tricky because the property will generate overloaded set_Property(double value)
		void set_Property(int value) { }
		double Property { get; set; }
	}
}
